/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jctools.queues; import static org.jctools.util.UnsafeAccess.UNSAFE; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.AbstractQueue; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Queue; import java.util.concurrent.ThreadLocalRandom; /** * Use an SPSC per producer. */ abstract class MpscOnSpscL0Pad<E> extends AbstractQueue<E> { long p00, p01, p02, p03, p04, p05, p06, p07; long p30, p31, p32, p33, p34, p35, p36, p37; } abstract class MpscOnSpscFields<E> extends MpscOnSpscL0Pad<E> { private final static long QUEUES_OFFSET; static { try { QUEUES_OFFSET = UNSAFE.objectFieldOffset(MpscOnSpscFields.class.getDeclaredField("queues")); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } } protected final ThreadLocal<Queue<E>> producerQueue; ReferenceQueue<Thread> refQ = new ReferenceQueue<Thread>(); protected volatile Queue<E>[] queues; private static class ThreadWeakRef extends WeakReference<Thread> { final Object r; public ThreadWeakRef(ReferenceQueue<? super Thread> q, Object r) { super(Thread.currentThread(), q); this.r = r; } } private List<ThreadWeakRef> weakRefHolder = Collections.synchronizedList(new ArrayList<ThreadWeakRef>()); @SuppressWarnings("unchecked") public MpscOnSpscFields(final int capacity) { producerQueue = new ThreadLocal<Queue<E>>() { @Override protected Queue<E> initialValue() { Queue<E> q = new SpscArrayQueue<E>(capacity); addQueue(q); return q; } }; Thread refProcessor = new Thread() { @Override public void run() { while(true) { try { ThreadWeakRef remove = (ThreadWeakRef)refQ.remove(); removeQueue(remove.r); weakRefHolder.remove(remove); } catch (InterruptedException e) { if (Thread.interrupted()) { return; } } } } }; refProcessor.setDaemon(false); refProcessor.start(); queues = new Queue[0]; } @SuppressWarnings("rawtypes") protected final void addQueue(Queue<E> q) { Queue[] oldQs; Queue[] newQs; do { oldQs = queues; newQs = new Queue[oldQs.length + 1]; System.arraycopy(oldQs, 0, newQs, 0, oldQs.length); newQs[oldQs.length] = q; } while (!UNSAFE.compareAndSwapObject(this, QUEUES_OFFSET, oldQs, newQs)); weakRefHolder.add(new ThreadWeakRef(refQ, q)); } @SuppressWarnings("rawtypes") protected final void removeQueue(Object q) { Queue[] oldQs; Queue[] newQs; do { oldQs = queues; int i = 0; final int length = oldQs.length; for (; i < length; i++) { if (q == oldQs[i]) { break; } } // not here... if (i == length) { return; } // copy over all but that element newQs = new Queue[length - 1]; System.arraycopy(oldQs, 0, newQs, 0, i); System.arraycopy(oldQs, i + 1, newQs, i, length - i - 1); } while (!UNSAFE.compareAndSwapObject(this, QUEUES_OFFSET, oldQs, newQs)); } int numberOfQueues(){ return queues.length; } } public final class MpscOnSpscQueue<E> extends MpscOnSpscFields<E>implements Queue<E> { long p40, p41, p42, p43, p44, p45, p46; long p30, p31, p32, p33, p34, p35, p36, p37; public MpscOnSpscQueue(final int capacity) { super(capacity); } @Override public boolean offer(final E e) { return producerQueue.get().offer(e); } @Override public E poll() { Queue<E>[] qs = this.queues; final int start = ThreadLocalRandom.current().nextInt(qs.length); for (int i = start; i < qs.length; i++) { E e = qs[i].poll(); if (e != null) return e; } for (int i = 0; i < start; i++) { E e = qs[i].poll(); if (e != null) return e; } return null; } @Override public E peek() { throw new UnsupportedOperationException(); } @Override public int size() { Queue<E>[] queues = this.queues; int sum = 0; for (Queue<E> q : queues) { sum += q.size(); } return sum; } @Override public boolean isEmpty() { Queue<E>[] queues = this.queues; for (Queue<E> q : queues) { if (!q.isEmpty()) { return false; } } return true; } @Override public Iterator<E> iterator() { throw new UnsupportedOperationException(); } }